﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
\* ######################################## */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AJRavindiran.Jolt.RuneScape.Network.Streaming
{
    /// <summary>
    /// Represents a stream reader.
    /// </summary>
    public class StreamReader
    {
        #region Fields
        private int readOffset = 0;
        private byte[] inBuffer;
        #endregion Fields

        #region Properties
        public int ReadOffset
        {
            get { return this.readOffset; }
            set { this.readOffset = value; }
        }

        public byte[] InBuffer
        {
            get { return this.inBuffer; }
        }
        #endregion Properties

        #region Constructor
        /// <summary>
        /// Constructs a new stream reader.
        /// </summary>
        /// <param name="inCapacity"></param>
        public StreamReader(int inCapacity)
        {
            this.inBuffer = new byte[inCapacity];
        }
        #endregion Constructor

        #region Methods
        public sbyte ReadSByte()
        {
            return (sbyte)(inBuffer[readOffset++]);
        }

        public byte ReadByte()
        {
            return (byte)(ReadSByte() & 0xff);
        }

        public sbyte ReadSByteA()
        {
            return (sbyte)(ReadSByte() - 128);
        }

        public sbyte ReadSByteC()
        {
            return (sbyte)(-ReadSByte());
        }

        public sbyte ReadSByteS()
        {
            return (sbyte)(128 - ReadSByte());
        }

        public byte ReadByteA()
        {
            return (byte)(ReadByte() - 128 & 0xff);
        }

        public byte ReadByteC()
        {
            return (byte)-(ReadByte() & 0xff);
        }

        public byte ReadByteS()
        {
            return (byte)(128 - ReadByte() & 0xff);
        }

        public void ReadBytes(sbyte[] array, int endOffset, int startOffset)
        {
            for (int i = startOffset; i < startOffset + endOffset; i++)
                array[i] = ReadSByte();
        }

        public void ReadBytesReserve(sbyte[] array, int endOffset, int startOffset)
        {
            for (int i = (startOffset + endOffset) - 1; i >= startOffset; i--)
                array[i] = ReadSByte();
        }

        public void ReadBytesReserveA(sbyte[] array, int endOffset, int startOffset)
        {
            for (int i = (startOffset + endOffset) - 1; i >= startOffset; i--)
                array[i] = ReadSByteA();
        }

        public short ReadShortBigEndian()
        {
            readOffset += 2;
            int i = ((inBuffer[readOffset - 1] & 0xff) << 8) + (inBuffer[readOffset - 2] & 0xff);
            if (i > 32767)
                i -= 0x10000;
            return (short)i;
        }

        public short ReadShortA()
        {
            readOffset += 2;
            int i = ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] - 128 & 0xff);
            if (i > 32767)
                i -= 0x10000;
            return (short)i;
        }

        public short ReadShortBigEndianA()
        {
            readOffset += 2;
            int i = ((inBuffer[readOffset - 1] & 0xff) << 8) + (inBuffer[readOffset - 2] - 128 & 0xff);
            if (i > 32767)
                i -= 0x10000;
            return (short)i;
        }

        public int ReadUShortBigEndian()
        {
            readOffset += 2;
            return ((inBuffer[readOffset - 1] & 0xff) << 8) + (inBuffer[readOffset - 2] & 0xff);
        }

        public int ReadUShort()
        {
            readOffset += 2;
            return ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] & 0xff);
        }

        public int ReadUShortA()
        {
            readOffset += 2;
            return ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] - 128 & 0xff);
        }

        public int ReadUShortBigEndianA()
        {
            readOffset += 2;
            return ((inBuffer[readOffset - 1] & 0xff) << 8) + (inBuffer[readOffset - 2] - 128 & 0xff);
        }

        public int ReadIntV1()
        {
            readOffset += 4;
            return ((inBuffer[readOffset - 2] & 0xff) << 24) + ((inBuffer[readOffset - 1] & 0xff) << 16) + ((inBuffer[readOffset - 4] & 0xff) << 8) + (inBuffer[readOffset - 3] & 0xff);
        }

        public int ReadIntV2()
        {
            readOffset += 4;
            return ((inBuffer[readOffset - 3] & 0xff) << 24) + ((inBuffer[readOffset - 4] & 0xff) << 16) + ((inBuffer[readOffset - 1] & 0xff) << 8) + (inBuffer[readOffset - 2] & 0xff);
        }

        public int ReadShort()
        {
            readOffset += 2;
            int i = ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] & 0xff);
            if (i > 32767)
                i -= 0x10000;
            return i;
        }

        public int ReadInt()
        {
            readOffset += 4;
            return ((inBuffer[readOffset - 4] & 0xff) << 24) + ((inBuffer[readOffset - 3] & 0xff) << 16) + ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] & 0xff);
        }

        public long ReadLong()
        {
            long l = (long)ReadInt() & 0xffffffffL;
            long l1 = (long)ReadInt() & 0xffffffffL;
            return (l << 32) + l1;
        }

        public long ReadLongV2()
        {
            readOffset += 8;
            return (((inBuffer[readOffset - 8] & 0xff) << 56) + ((inBuffer[readOffset - 7] & 0xff) << 48) + ((inBuffer[readOffset - 6] & 0xff) << 40) + ((inBuffer[readOffset - 5] & 0xff) << 32) + ((inBuffer[readOffset - 4] & 0xff) << 24) + ((inBuffer[readOffset - 3] & 0xff) << 16) + ((inBuffer[readOffset - 2] & 0xff) << 8) + (inBuffer[readOffset - 1] & 0xff));
        }

        public string ReadString()
        {
            StringBuilder sb = new StringBuilder();
            sbyte b;
            while ((b = ReadSByte()) != 0)
                sb.Append((char)b);

            return sb.ToString();
        }

        public int Read3Bytes()
        {
            return (ReadByte() << 16) | (ReadByte() << 8) | ReadByte();
        }

        public int Read2Bytes()
        {
            return (ReadByte() << 8) + ReadByte();
        }
        #endregion Methods
    }
}
